package com.lz.autotest.project.testmanage.infmanage.service.impl;

import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import com.lz.autotest.common.constant.Constants;
import com.lz.autotest.common.constant.HttpStatus;
import com.lz.autotest.common.entity.ResponseVo;
import com.lz.autotest.common.utils.DataMap;
import com.lz.autotest.common.utils.OkHttpUtil;
import com.lz.autotest.common.utils.SysUtils;
import com.lz.autotest.common.utils.ip.IpUtils;
import com.lz.autotest.project.testmanage.autoTest.entity.ResultView;
import com.lz.autotest.project.testmanage.infmanage.domain.InfResult;
import com.lz.autotest.project.testmanage.infmanage.domain.SstInf;
import com.lz.autotest.project.testmanage.reportmanage.service.InfReportService;
import com.lz.autotest.project.testmanage.infmanage.service.InfService;
import com.lz.autotest.project.testmanage.envmanage.service.TsstEnvService;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.ExecutorCompletionService;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

/**
 * 接口管理 服务实现
 *
 * @author lz
 */
@Service
public class InfServiceImpl implements InfService {
    private static final Logger logger = LoggerFactory.getLogger(InfService.class);

    @Resource
    private InfReportService infReportService;
    @Resource
    private TsstEnvService tsstEnvService;

    /**
     * 请求接口
     */
    @Override
    public ResponseVo requestServe(SstInf sstInf) {
        if (sstInf.getInfRealEnv() == null) {
            sstInf.setInfRealEnv(tsstEnvService.selectTsstEnvByEnvId(Long.valueOf(sstInf.getInfEnv())));
            if (sstInf.getInfRealEnv().toLowerCase().contains("localhost")) {
                String ip = IpUtils.getHostIp();
                sstInf.setInfRealEnv(sstInf.getInfRealEnv().toLowerCase().replace("localhost", ip));
            }
        }
        sstInf.setInfFullUrl(sstInf.getInfRealEnv() + sstInf.getInfUrl());
        ResponseVo responseVo = new ResponseVo();
        InfResult infResult = new InfResult();

        try {
            switch (sstInf.getInfMethod()) {
                case Constants.POST:
                    responseVo = OkHttpUtil.requestPostVo(sstInf);
                    break;
                case Constants.GET:
                    responseVo = OkHttpUtil.requestGetVo(sstInf);
                    break;
                default:
                    responseVo = OkHttpUtil.requestPostVo(sstInf);
                    break;
            }
            infResult.setCount(1);
            infResult.setReportType("S");
            infResult.setRequestTime(sstInf.getRequestTime());
            infResult.setResponseTime(sstInf.getResponseTime());
            infResult.setConsumeTime(sstInf.getConsumeTime());
            if (SysUtils.isJson(responseVo.getMsg())) {
                infResult.setResponse(responseVo.getMsg());
            } else {
                infResult.setResponse(JSONUtil.toJsonStr(responseVo));
            }
            responseVo.setResult(infResult);
            if (responseVo.getCode() == 200 && sstInf.getVariableSwitch() == 1) {
                JSONObject jsonObject = JSONUtil.parseObj(infResult.getResponse());
                Object lastObj = null;
                if (SysUtils.isNotVoid(sstInf.getVariableEl()) && sstInf.getVariableEl().indexOf("$") == 0) {
                    String[] els = sstInf.getVariableEl().split("\\.");
                    for (int i = 1; i < els.length; i++) {
                        String el = els[i];
                        if (i == (els.length - 1)) {
                            lastObj = jsonObject.getObj(el);
                        } else {
                            jsonObject = jsonObject.getJSONObject(el);
                        }
                    }
                }
                if (lastObj != null) {
                    String newObj = lastObj.toString();
                    if (SysUtils.isNotVoid(DataMap.get(sstInf.getInfRealEnv()))) {
                        Map<String, Object> map = DataMap.get(sstInf.getInfRealEnv());
                        map.put(sstInf.getVariableName(), newObj);
                    } else {
                        Map<String, Object> map = new HashMap<>();
                        map.put(sstInf.getVariableName(), newObj);
                        DataMap.put(sstInf.getInfRealEnv(), map);
                    }
                }
            }
        } catch (Exception e) {
            responseVo.setCode(HttpStatus.ERROR);
            responseVo.setMsg(e.getMessage());
            logger.error("接口调用失败：{}", e.getMessage());
        }
        return responseVo;
    }

    @Override
    public ResultView requestServeByAutoTest(SstInf sstInf) {
        sstInf.setInfFullUrl(sstInf.getInfRealEnv() + sstInf.getInfUrl());
        ResultView res = null;
        switch (sstInf.getInfMethod()) {
            case Constants.POST:
                res = OkHttpUtil.requestPostByAT(sstInf);
                break;
            default:
                res = OkHttpUtil.requestGetByAT(sstInf);
                break;
        }
        return res;
    }

    public ResponseVo requestServeBatch(List<SstInf> list) {
        ResponseVo responseVo = new ResponseVo();
        for (SstInf sstInf : list) {
            responseVo = requestServe(sstInf);
        }
        return responseVo;
    }

    @Override
    public ResponseVo requestServeBatch(SstInf sstInf) {

        ResponseVo responseAll = ResponseVo.success();
        responseAll.setSole(sstInf.getSnowflakeId());
        InfResult infResponse = new InfResult();
        ExecutorService fixedThreadPool = Executors.newFixedThreadPool(sstInf.getConcurrency());
        ExecutorCompletionService<ResponseVo> completionService = new ExecutorCompletionService<>(fixedThreadPool);

        try {
            infResponse.setRequestTime(sstInf.getRequestTime());

            infResponse = requestByEntry(sstInf, completionService);

            infResponse.setResponseTime(sstInf.getResponseTime());
            //处理结果
            infResponse.setInfId(sstInf.getInfId());
            infResponse.setInfEnv(sstInf.getInfEnv());
            infResponse.setInfName(sstInf.getInfName());
            infResponse.setInfUrl(sstInf.getInfUrl());
            infResponse.setInfMethod(sstInf.getInfMethod());
            infResponse.setCount((long) sstInf.getConcurrency() * sstInf.getRound());
            infResponse.setRound(sstInf.getRound());
            infResponse.setConcurrency(sstInf.getConcurrency());
            DoubleSummaryStatistics stats = infResponse.getResponseVoList().stream()
                    .mapToDouble((x) -> ((InfResult) x.getResult()).getConsumeTime())
                    .summaryStatistics();
            infResponse.setMaxConsumeTime(stats.getMax());
            infResponse.setMinConsumeTime(stats.getMin());
            infResponse.setAvgConsumeTime(stats.getAverage());
            responseAll.setResult(infResponse);
        } catch (Exception ex) {
            responseAll.setCode(-1);
            responseAll.setMsg(ex.getMessage());
            logger.error("接口批量调用失败：{}", ex.getMessage());
        } finally {
            fixedThreadPool.shutdown();
            infReportService.saveInfReportAsy(responseAll, sstInf);
        }

        return responseAll;
    }

    @Override
    public ResponseVo exportReport(SstInf sstInf) {
        return null;
    }


    /**
     * 根据输入参数请求接口
     */
    public InfResult requestByEntry(SstInf sstInf, ExecutorCompletionService completionService) {
        List<ResponseVo> responseList = new ArrayList<>();
        InfResult infResponse = new InfResult();
        long start = System.currentTimeMillis();

        try {
            for (int k = 0; k < sstInf.getRound(); k++) {

                for (int i = 0; i < sstInf.getConcurrency(); i++) {
                    completionService.submit(() -> {
                        ResponseVo responseVo = null;
                        try {
                            responseVo = requestServe(sstInf);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        return responseVo;
                    });
                }
                for (int i = 0; i < sstInf.getConcurrency(); i++) {
                    Future<ResponseVo> future = completionService.take();
                    if (future != null) {
                        if (future.get().getCode() < 0) {
                            infResponse.setFailCount(infResponse.getFailCount() + 1);
                        } else {
                            infResponse.setSuccessCount(infResponse.getSuccessCount() + 1);
                        }
                        responseList.add(future.get());
                    }
                }
            }
            infResponse.setResponseVoList(responseList);
        } catch (Exception ex) {
            infResponse.setSuccess(false);
            infResponse.setMsg(ex.getMessage());
            logger.error("接口批量调用失败：" + ex.getMessage());
            ex.printStackTrace();
        }
        long end = System.currentTimeMillis();
        infResponse.setConsumeTime(end - start);

        return infResponse;
    }

}